package cn.uncode.dal.core;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;

import cn.uncode.dal.asyn.AsynContext;
import cn.uncode.dal.asyn.AsynSQLTask;
import cn.uncode.dal.asyn.Method;
import cn.uncode.dal.cache.CacheManager;
import cn.uncode.dal.criteria.Model;
import cn.uncode.dal.criteria.QueryCriteria;
import cn.uncode.dal.criteria.QueryCriteria.Criteria;
import cn.uncode.dal.descriptor.Column;
import cn.uncode.dal.descriptor.ForeignKey;
import cn.uncode.dal.descriptor.QueryResult;
import cn.uncode.dal.descriptor.Table;
import cn.uncode.dal.descriptor.db.ResolveDataBase;
import cn.uncode.dal.descriptor.resolver.JavaType;
import cn.uncode.dal.descriptor.resolver.JavaTypeConversion;
import cn.uncode.dal.descriptor.resolver.JavaTypeResolver;
import cn.uncode.dal.event.EventManager;
import cn.uncode.dal.event.asyn.EventContext;
import cn.uncode.dal.exception.DalSqlException;
import cn.uncode.dal.internal.util.message.Messages;
import cn.uncode.dal.router.DefaultMasterSlaveRouter;
import cn.uncode.dal.router.MasterSlaveRouter;
import cn.uncode.dal.router.TableShardingRouter;
import cn.uncode.dal.utils.BeanUtil;
import cn.uncode.dal.utils.JsonUtils;
import cn.uncode.dal.utils.ShardsUtils;

public abstract class AbstractBaseDAL implements BaseDAL {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractBaseDAL.class);

    public static final String CACHE_KEY_PREFIX = "uncode_dal_";
    private static final String CACHE_KEY_SELECT_BY_CRITERIA = "_selectByCriteria_";
    private static final String CACHE_KEY_COUNT_BY_CRITERIA = "_countByCriteria_";
    private static final String CACHE_KEY_SELECT_PAGE_BY_CRITERIA = "_selectPageByCriteria_";
    private static final String CACHE_KEY_SELECT_BY_IDS = "_selectPageByIds_";
    public static final String CACHE_KEY_SELECT_BY_PRIMARY_KEY = "_selectByPrimaryKey_";

    protected CacheManager cacheManager;

    protected ResolveDataBase resolveDatabase;

    protected MasterSlaveRouter router = new DefaultMasterSlaveRouter();

    protected boolean useCache = true;

    protected String version;

    protected List<String> versionTables;

    protected List<String> listenerNames;

    /**
     * 采用“需要缓存的”来筛选
     */
    protected boolean useCacheFilter = true;

    protected List<String> noCacheTables;

    protected List<String> cacheTables;

    //-------------------------
    // 异步
    //-------------------------
    /**
     * 日志队列
     */
    private BlockingQueue<AsynContext> sqlQueue;
    /**
     * 写线程池
     */
    private ExecutorService asynWriterService;
    
    /**
     * 异步日志线程池大小
     */
    private int asynWriterThreadSize = 5;
    private List<AsynSQLTask> tasks = new ArrayList<AsynSQLTask>();
    

    public AbstractBaseDAL() {
        noCacheTables = new ArrayList<String>();
        cacheTables = new ArrayList<String>();
        sqlQueue = new LinkedBlockingQueue<AsynContext>();
        asynWriterService = Executors.newFixedThreadPool(asynWriterThreadSize);
//        for (int i = 0; i < asynWriterThreadSize; i++) {
            AsynSQLTask task = new AsynSQLTask(this);
            task.setLogQueue(sqlQueue);
            tasks.add(task);
            asynWriterService.submit(task);
//        }
        LOG.info("Asyn dal init ok!");
    }
    
    //-------------------------------------------------------------------
  	// 抽象方法
  	//-------------------------------------------------------------------
    
    public abstract List<Map<String, Object>> _selectByCriteria(final Table table);

    public abstract int _countByCriteria(final Table table);
    
    public abstract Map<String, Object> _selectByPrimaryKey(final Table table);
    
    public abstract long _insert(Table table);
    
    public abstract int _insertList(Table table);
    
    public abstract int _updateByCriteria(Table table);
    
    public abstract int _updateByPrimaryKey(Table table);
    
    public abstract int _deleteByPrimaryKey(Table table);
    
    public abstract int _deleteByCriteria(Table table);

	//-------------------------------------------------------------------
	// 业务核心方法实现-selectPageByCriteria
	//-------------------------------------------------------------------
    @Override
    public QueryResult selectPageByCriteria(QueryCriteria queryCriteria) {
        return selectPageByCriteria(queryCriteria, 5000);
    }
    
    @Override
    public QueryResult selectPageByCriteria(String[] fields, QueryCriteria queryCriteria) {
        return selectPageByCriteria(fields, queryCriteria, 5000);
    }

    @Override
    public QueryResult selectPageByCriteria(List<String> fields, QueryCriteria queryCriteria) {
        return selectPageByCriteria(fields, queryCriteria, 5000);
    }

    @Override
    public QueryResult selectPageByCriteria(QueryCriteria queryCriteria, int seconds) {
    	String cacheKey = generateMultiSelectCacheKey(null, queryCriteria, CACHE_KEY_SELECT_PAGE_BY_CRITERIA);
    	QueryResult queryResult = null;
    	if (TableShardingRouter.containsTable(queryCriteria.getTable())) {
    		queryResult = ShardsUtils.shardingSelectPageByCriteria(asynWriterService, this, queryCriteria, null);
        } else {
        	int total = countByCriteria(queryCriteria, seconds);
            if (total > 0) {
                int pageCount = total / queryCriteria.getPageSize();
                if (total % queryCriteria.getPageSize() != 0) {
                    pageCount++;
                }
                if (queryCriteria.getPageIndex() > pageCount) {
                    queryCriteria.setPageIndex(pageCount);
                }
                queryResult = selectByCriteria(queryCriteria, seconds);
                Map<String, Object> page = new HashMap<String, Object>();
                page.put(PAGE_INDEX_KEY, queryCriteria.getPageIndex());
                page.put(PAGE_SIZE_KEY, queryCriteria.getPageSize());
                page.put(PAGE_COUNT_KEY, pageCount);
                page.put(RECORD_TOTAL_KEY, total);
                queryResult.setPage(page);
            }
        }
    	if (queryResult != null) {
    		if (cacheManager != null && cacheManager.getCache() != null && useCache) {
    			cacheManager.getCache().putObject(buildTableCacheSpace(queryCriteria), cacheKey, queryResult, seconds);
            }
        }
    	
    	return queryResult;
    }


    @Override
    public QueryResult selectPageByCriteria(String[] fields, QueryCriteria queryCriteria, int seconds) {
    	QueryResult queryResult = null;
    	String cacheKey = generateMultiSelectCacheKey(null, queryCriteria, CACHE_KEY_SELECT_PAGE_BY_CRITERIA);
    	if (TableShardingRouter.containsTable(queryCriteria.getTable())) {
    		queryResult = ShardsUtils.shardingSelectPageByCriteria(asynWriterService, this, queryCriteria, Arrays.asList(fields));
        } else {
        	int total = countByCriteria(queryCriteria, seconds);
            if (total > 0) {
                int pageCount = total / queryCriteria.getPageSize();
                if (total % queryCriteria.getPageSize() != 0) {
                    pageCount++;
                }
                if (queryCriteria.getPageIndex() > pageCount) {
                    queryCriteria.setPageIndex(pageCount);
                }
                queryResult = selectByCriteria(fields, queryCriteria, seconds);
                Map<String, Object> page = new HashMap<String, Object>();
                page.put(PAGE_INDEX_KEY, queryCriteria.getPageIndex());
                page.put(PAGE_SIZE_KEY, queryCriteria.getPageSize());
                page.put(PAGE_COUNT_KEY, pageCount);
                page.put(RECORD_TOTAL_KEY, total);
                queryResult.setPage(page);
            }
        }
    	if (queryResult != null) {
    		if (cacheManager != null && cacheManager.getCache() != null && useCache) {
    			cacheManager.getCache().putObject(buildTableCacheSpace(queryCriteria), cacheKey, queryResult, seconds);
            }
        }
        return queryResult;

    }

    @Override
    public QueryResult selectPageByCriteria(List<String> fields, QueryCriteria queryCriteria, int seconds) {
    	QueryResult queryResult = null;
    	String cacheKey = generateMultiSelectCacheKey(null, queryCriteria, CACHE_KEY_SELECT_PAGE_BY_CRITERIA);
    	if (TableShardingRouter.containsTable(queryCriteria.getTable())) {
    		queryResult = ShardsUtils.shardingSelectPageByCriteria(asynWriterService, this, queryCriteria, fields);
        } else {
        	int total = countByCriteria(queryCriteria, seconds);
            if (total > 0) {
                int pageCount = total / queryCriteria.getPageSize();
                if (total % queryCriteria.getPageSize() != 0) {
                    pageCount++;
                }
                if (queryCriteria.getPageIndex() > pageCount) {
                    queryCriteria.setPageIndex(pageCount);
                }
                queryResult = selectByCriteria(fields, queryCriteria, seconds);
                Map<String, Object> page = new HashMap<String, Object>();
                page.put(PAGE_INDEX_KEY, queryCriteria.getPageIndex());
                page.put(PAGE_SIZE_KEY, queryCriteria.getPageSize());
                page.put(PAGE_COUNT_KEY, pageCount);
                page.put(RECORD_TOTAL_KEY, total);
                queryResult.setPage(page);
            }
        }
    	if (queryResult != null) {
    		if (cacheManager != null && cacheManager.getCache() != null && useCache) {
    			cacheManager.getCache().putObject(buildTableCacheSpace(queryCriteria), cacheKey, queryResult, seconds);
            }
        }
        return queryResult;
    }
    
	//-------------------------------------------------------------------
	// 业务核心方法实现-selectByCriteria
	//-------------------------------------------------------------------
    @Override
    public QueryResult selectByCriteria(String[] fields, QueryCriteria queryCriteria) {
        return selectByCriteria(fields, queryCriteria, PERSISTENT_CACHE);
    }
    
    @Override
    public QueryResult selectByCriteria(List<String> fields, QueryCriteria queryCriteria) {
        return selectByCriteria(fields, queryCriteria, PERSISTENT_CACHE);
    }

    @Override
    public QueryResult selectByCriteria(QueryCriteria queryCriteria) {
        return selectByCriteria(queryCriteria, PERSISTENT_CACHE);
    }

    @Override
    public QueryResult selectByCriteria(String[] fields, QueryCriteria queryCriteria, int seconds) {
        EventManager.getInstance().sendEvent(new EventContext(Method.SELECT_BY_CRITERIA, true, Arrays.asList(fields), queryCriteria, seconds, false));
        if (router != null) {
            router.routeToSlave();
        }
        QueryResult queryResult = new QueryResult();
        String tableName = queryCriteria.getTable();
        String cacheKey = generateMultiSelectCacheKey(Arrays.asList(fields), queryCriteria, CACHE_KEY_COUNT_BY_CRITERIA);
        if (isCacheTable(seconds,tableName)) {
            @SuppressWarnings("unchecked")
            List<Map<String, Object>> value = (List<Map<String, Object>>) cacheManager.getCache().getObject(buildTableCacheSpace(queryCriteria), cacheKey);
            if (value != null && value.size() > 0) {
                queryResult.setResultList(value);
                EventManager.getInstance().sendEvent(new EventContext(Method.SELECT_BY_CRITERIA, false, queryResult, true));
                return queryResult;
            }
        }
        Table table = retrievalTableByQueryCriteria(queryCriteria);

        buildSelectFields(fields, table);

        List<Map<String, Object>> result = null;
        if (TableShardingRouter.containsTable(tableName)) {
        	List<String> list = Arrays.asList(fields);
            if(queryCriteria.getShardTransaction()){
                queryResult = ShardsUtils.shardingSelectByCriteria(this, queryCriteria, list);
            }else{
                queryResult = ShardsUtils.shardingSelectByCriteria(asynWriterService, this, queryCriteria, list);
            }
        	result = queryResult.getList();
        } else {
            table.setQueryCriteria(queryCriteria);
            result = _selectByCriteria(table);
        }

        if (result != null) {
            //查询结果存在,才进行缓存
            if (isCacheResult(seconds,tableName,result.size())) {
                if (seconds > 0) {
                    cacheManager.getCache().putObject(buildTableCacheSpace(queryCriteria), cacheKey, result, seconds);
                } else {
                    cacheManager.getCache().putObject(buildTableCacheSpace(queryCriteria), cacheKey, result);
                }
            }
            queryResult.setResultList(result);
            EventManager.getInstance().sendEvent(new EventContext(Method.SELECT_BY_CRITERIA, false, queryResult, false));
        }
        return queryResult;
    }

    @Override
    public QueryResult selectByCriteria(List<String> fields, QueryCriteria queryCriteria, int seconds) {
        EventManager.getInstance().sendEvent(new EventContext(Method.SELECT_BY_CRITERIA, true, fields, queryCriteria, seconds, false));
        if (router != null) {
            router.routeToSlave();
        }
        QueryResult queryResult = new QueryResult();
        String tableName = queryCriteria.getTable();
        String cacheKey = generateMultiSelectCacheKey(fields, queryCriteria, CACHE_KEY_SELECT_BY_CRITERIA);
        boolean isTableCache = isCacheTable(seconds, tableName);
        if (isTableCache) {
            @SuppressWarnings("unchecked")
            List<Map<String, Object>> value = (List<Map<String, Object>>) cacheManager.getCache().getObject(buildTableCacheSpace(queryCriteria), cacheKey);
            if (value != null && value.size() > 0) {
                queryResult.setResultList(value);
                EventManager.getInstance().sendEvent(new EventContext(Method.SELECT_BY_CRITERIA, false, queryResult, true));
                return queryResult;
            }
        }
        Table table = retrievalTableByQueryCriteria(queryCriteria);

        buildSelectFields(fields, table);

        List<Map<String, Object>> result = null;
        if (TableShardingRouter.containsTable(tableName)) {
            if(queryCriteria.getShardTransaction()){
                queryResult = ShardsUtils.shardingSelectByCriteria(this, queryCriteria, fields);
            }else{
                queryResult = ShardsUtils.shardingSelectByCriteria(asynWriterService, this, queryCriteria, fields);
            }
        	result = queryResult.getList();
        } else {
            table.setQueryCriteria(queryCriteria);
            result = _selectByCriteria(table);
        }


        if (result != null) {
            //查询结果存在,才进行缓存
            if (isCacheResult(isTableCache,result.size())) {
                if (seconds > 0) {
                    cacheManager.getCache().putObject(buildTableCacheSpace(queryCriteria), cacheKey, result, seconds);
                } else {
                    cacheManager.getCache().putObject(buildTableCacheSpace(queryCriteria), cacheKey, result);
                }
            }
            queryResult.setResultList(result);
            EventManager.getInstance().sendEvent(new EventContext(Method.SELECT_BY_CRITERIA, false, queryResult, false));
        }
        return queryResult;
    }
    
    @Override
    public QueryResult selectByCriteria(QueryCriteria queryCriteria, int seconds) {
        EventManager.getInstance().sendEvent(new EventContext(Method.SELECT_BY_CRITERIA, true, null, queryCriteria, seconds, false));
        if (router != null) {
            router.routeToSlave();
        }
        QueryResult queryResult = new QueryResult();
        String tableName = queryCriteria.getTable();
        String cacheKey = generateMultiSelectCacheKey(null, queryCriteria, CACHE_KEY_COUNT_BY_CRITERIA);
        if (isCacheTable(seconds,tableName)) {
            @SuppressWarnings("unchecked")
            List<Map<String, Object>> value = (List<Map<String, Object>>) cacheManager.getCache().getObject(buildTableCacheSpace(queryCriteria), cacheKey);
            if (value != null && value.size() > 0) {
                queryResult.setResultList(value);
                EventManager.getInstance().sendEvent(new EventContext(Method.SELECT_BY_CRITERIA, false, queryResult, true));
                return queryResult;
            }
        }
        Table table = retrievalTableByQueryCriteria(queryCriteria);

        List<Map<String, Object>> result = null;
        if (TableShardingRouter.containsTable(tableName)) {
            if(queryCriteria.getShardTransaction()){
                queryResult = ShardsUtils.shardingSelectByCriteria(this, queryCriteria, null);
            }else{
                queryResult = ShardsUtils.shardingSelectByCriteria(asynWriterService, this, queryCriteria, null);
            }
        	result = queryResult.getList();
        } else {
            table.setQueryCriteria(queryCriteria);
            result = _selectByCriteria(table);
        }


        if (result != null) {
            //查询结果存在,才进行缓存
            if (isCacheResult(seconds,tableName,result.size())) {
                if (seconds > 0) {
                    cacheManager.getCache().putObject(buildTableCacheSpace(queryCriteria), cacheKey, result, seconds);
                } else {
                    cacheManager.getCache().putObject(buildTableCacheSpace(queryCriteria), cacheKey, result);
                }
            }
            queryResult.setResultList(result);
            EventManager.getInstance().sendEvent(new EventContext(Method.SELECT_BY_CRITERIA, false, queryResult, false));

        }

        return queryResult;

    }

	//-------------------------------------------------------------------
	// 业务核心方法实现-countByCriteria
	//-------------------------------------------------------------------

    @Override
    public int countByCriteria(QueryCriteria queryCriteria, int seconds) {
        return countByCriteria(null, queryCriteria, seconds);
    }

    @Override
    public int countByCriteria(QueryCriteria queryCriteria) {
        return countByCriteria(queryCriteria, PERSISTENT_CACHE);
    }

    @Override
    public int countByCriteria(List<String> fields, QueryCriteria queryCriteria) {
        return countByCriteria(fields, queryCriteria, PERSISTENT_CACHE);
    }

    @Override
    public int countByCriteria(List<String> fields, QueryCriteria queryCriteria, int seconds) {
        EventManager.getInstance().sendEvent(new EventContext(Method.COUNT_BY_CRITERIA, true, fields, queryCriteria, seconds, false));
        if (router != null) {
            router.routeToSlave();
        }
        String cacheKey = generateMultiSelectCacheKey(fields, queryCriteria, CACHE_KEY_COUNT_BY_CRITERIA);
        if (isCacheTable(seconds,queryCriteria.getTable())) {
            Integer value = (Integer) cacheManager.getCache().getObject(buildTableCacheSpace(queryCriteria), cacheKey);
            if (value != null && value >= 0) {
                EventManager.getInstance().sendEvent(new EventContext(Method.COUNT_BY_CRITERIA, false, value, true));
                return value;
            }
        }
        Table table = retrievalTableByQueryCriteria(queryCriteria);

        int result = 0;
        if (TableShardingRouter.containsTable(queryCriteria.getTable())) {
            result = ShardsUtils.shardingCount(asynWriterService,this,queryCriteria);
        } else {
            table.setQueryCriteria(queryCriteria);
            result = _countByCriteria(table);
        }

        if (isCacheResult(seconds,queryCriteria.getTable(),result)) {
            if (seconds > 0) {
                cacheManager.getCache().putObject(buildTableCacheSpace(queryCriteria), cacheKey, result, seconds);
            } else {
                cacheManager.getCache().putObject(buildTableCacheSpace(queryCriteria), cacheKey, result);
            }
        }
        EventManager.getInstance().sendEvent(new EventContext(Method.COUNT_BY_CRITERIA, false, result, false));
        return result;
    }

	//-------------------------------------------------------------------
	// 业务核心方法实现-selectByPrimaryKey
	//-------------------------------------------------------------------

    @Override
    public QueryResult selectByPrimaryKey(Object obj) {
        return selectByPrimaryKey(new Model(obj), PERSISTENT_CACHE);
    }

    @Override
    public QueryResult selectByPrimaryKey(String[] fields, Object obj) {
        return selectByPrimaryKey(fields, obj, PERSISTENT_CACHE);
    }

    @Override
    public QueryResult selectByPrimaryKey(String[] fields, Object obj, int seconds) {
        return selectByPrimaryKey(fields, new Model(obj), seconds);
    }

    @Override
    public QueryResult selectByPrimaryKey(List<String> fields, Object obj) {
        return selectByPrimaryKey(fields, obj, PERSISTENT_CACHE);
    }

    @Override
    public QueryResult selectByPrimaryKey(List<String> fields, Object obj, int seconds) {
        return selectByPrimaryKey(fields, new Model(obj), seconds);
    }

    @Override
    public QueryResult selectByPrimaryKey(Class<?> clazz, Object id) {
        return selectByPrimaryKey(null, clazz, id, PERSISTENT_CACHE);
    }

    @Override
    public QueryResult selectByPrimaryKey(Class<?> clazz, Object id, int seconds) {
        return selectByPrimaryKey(null, clazz, id, seconds);
    }

    @Override
    public QueryResult selectByPrimaryKey(List<String> fields, Class<?> clazz, Object id) {
        return selectByPrimaryKey(fields, clazz, id, PERSISTENT_CACHE);
    }

    @Override
    public QueryResult selectByPrimaryKey(List<String> fields, Class<?> clazz, Object id, int seconds) {
        if (id == null) {
            return new QueryResult();
        }
        Model model = new Model(clazz);
        model.setSinglePrimaryKey(id);
        return selectByPrimaryKey(fields, model, seconds);
    }

    @Override
    public QueryResult selectByPrimaryKey(String table, Object id) {
        List<String> fields = null;
        Model model = new Model(table);
        model.setSinglePrimaryKey(id);
        return selectByPrimaryKey(fields, model, PERSISTENT_CACHE);
    }

    @Override
    public QueryResult selectByPrimaryKey(String table, Object id, int seconds) {
        List<String> fields = null;
        Model model = new Model(table);
        model.setSinglePrimaryKey(id);
        return selectByPrimaryKey(fields, model, seconds);
    }


    @Override
    public QueryResult selectByPrimaryKey(List<String> fields, String table, Object id) {
        Model model = new Model(table);
        model.setSinglePrimaryKey(id);
        return selectByPrimaryKey(fields, model, PERSISTENT_CACHE);
    }


    @Override
    public QueryResult selectByPrimaryKey(List<String> fields, String table, Object id, int seconds) {
        Model model = new Model(table);
        model.setSinglePrimaryKey(id);
        return selectByPrimaryKey(fields, model, seconds);
    }

    @Override
    public QueryResult selectByPrimaryKey(String[] fields, String database, Object obj, int seconds) {
        Model model = new Model(obj);
        model.setDatabase(database);
        return selectByPrimaryKey(fields, model, seconds);
    }

	private QueryResult selectByPrimaryKey(Model model, int seconds) {
        return selectByPrimaryKey(new String[] {}, model, seconds);
    }

	private QueryResult selectByPrimaryKey(String[] fields, Model model, int seconds) {
    	List<String> fdList = Arrays.asList(fields);
    	return selectByPrimaryKey(fdList, model, seconds);
    }

    @SuppressWarnings("unchecked")
	private QueryResult selectByPrimaryKey(List<String> fields, Model model, int seconds) {
        EventManager.getInstance().sendEvent(new EventContext(Method.SELECT_BY_PRIMARY_KEY, true, fields, model, seconds, false));
        if (model == null) {
            return null;
        }
        if (router != null) {
            router.routeToSlave();
        }

        if (TableShardingRouter.containsTable(model.getTableName())) {
            long id = Long.valueOf(model.getSinglePrimaryKey().toString());
            String shardingTable = TableShardingRouter.getShardingTableById(model.getTableName(), id);
            if (StringUtils.isNotEmpty(shardingTable)) {
                model.setTableName(shardingTable);
            }
        }

        QueryResult queryResult = new QueryResult();
        String cacheKey = generateSingleCacheKey(model);
        
        Map<String, Object> value = null;//ThreadLocalCacheManager.get(cacheKey);
        if(null == value){
        	if (isCacheTable(seconds,model.getTableName())) {
                value = (Map<String, Object>) cacheManager.getCache().getObject(buildTableCacheSpace(model), cacheKey);
                if (value != null && value.size() > 0) {
		        	//ThreadLocalCacheManager.putSelect(cacheKey, value);
    			}
        	}
        }
        
        if (value != null && value.size() > 0) {
            queryResult.setResultMap(value);
            EventManager.getInstance().sendEvent(new EventContext(Method.SELECT_BY_PRIMARY_KEY, false, fields, model, seconds, true));
            return queryResult;
        }
        
        Table table = retrievalTableByModel(model);
        Map<String, Object> result = commonSelectByPrimaryKey(model);
        if(table.getContent().hasForeignKeys()) {
        	getContentByForeignKey(table.getContent().getForeignKeys(), result, model);
        }

        if (result != null) {
        	//ThreadLocalCacheManager.putSelect(cacheKey, result);
            if (isCacheResult(seconds,model.getTableName(),result.size())) {
                if (seconds > 0) {
                    cacheManager.getCache().putObject(buildTableCacheSpace(model), cacheKey, result, seconds);
                } else {
                    cacheManager.getCache().putObject(buildTableCacheSpace(model), cacheKey, result);
                }
            }
            if (fields != null && fields.size() > 0) {
                Map<String, Object> resultMap = new HashMap<String, Object>();
                for (Entry<String, Object> item : result.entrySet()) {
                    if (fields.contains(item.getKey())) {
                        resultMap.put(item.getKey(), item.getValue());
                    }
                }
                queryResult.setResultMap(resultMap);
            } else {
                queryResult.setResultMap(result);
            }
            EventManager.getInstance().sendEvent(new EventContext(Method.SELECT_BY_PRIMARY_KEY, false, fields, model, seconds, false));
            return queryResult;
        } else {
            return null;
        }
    }
    
	private Map<String, Object> commonSelectByPrimaryKey(Model model) {
		Map<String, Object> result = new HashMap<>();
		Table table = retrievalTableByModel(model);
		if (model != null) {
			List<String> names = table.getPrimaryKey().getFields();
			if (null != model.getSinglePrimaryKey()) {
				LinkedHashMap<String, Object> condistions = new LinkedHashMap<String, Object>();
				if (null != names && names.size() > 0) {
					condistions.put(names.get(0), model.getSinglePrimaryKey());
				} else {
					condistions.put(BaseDTO.ID, model.getSinglePrimaryKey());
				}
				table.setConditions(condistions);
			} else {
				table.setConditions(model.getContent());
			}
		}
		result = _selectByPrimaryKey(table);
		return result;
	}
    
    private void getContentByForeignKey(Set<ForeignKey> foreignKeys, Map<String, Object> content, Model model) {
    	if(null != foreignKeys && null != content) {
    		for(ForeignKey foreignKey : foreignKeys) {
    			Model mod = model.newForeignKeyModel(foreignKey);
    			mod.setSinglePrimaryKey(content.get(foreignKey.getFdTableName().toLowerCase()));
    			Map<String, Object> fkObj = commonSelectByPrimaryKey(mod);
    			if(!ObjectUtils.isEmpty(fkObj)) {
    				content.put(foreignKey.getFdName(), fkObj);
    				if(foreignKey.hasForeignKey()) {
    					getContentByForeignKey(foreignKey.getForeignKeys(), fkObj, mod);
        			}
    			}
        	}
    	}
	}
    
    //-------------------------
  	// selectByIds
  	//-------------------------
    
    public QueryResult selectByIds(List<String> fields, String tableName, Object... ids){
    	return selectByIds(fields, null, tableName, ids);
    }
    
    public QueryResult selectByIds(List<String> fields, String tableName, List<Object> ids){
    	return selectByIds(fields, null, tableName, ids);
    }
    
    /**
     * 根据id查询
     * @param fields
     * @param tableName
     * @param ids
     * @return
     */
    public QueryResult selectByIds(List<String> fields, String database, String tableName, List<Object> ids){
        if (ids == null) {
            return null;
        }
        if (router != null) {
            router.routeToSlave();
        }
        
        List<Map<String, Object>> result = null;
        QueryResult queryResult = new QueryResult();
        
        String cacheKey = generateIdsCacheKey(database, tableName, ids);
        if (isCacheTable(0, tableName)) {
            @SuppressWarnings("unchecked")
            List<Map<String, Object>> value = (List<Map<String, Object>>) cacheManager.getCache().getObject(buildTableCacheSpace(database, tableName), cacheKey);
            if (value != null && value.size() > 0) {
                queryResult.setResultList(value);
                return queryResult;
            }
        }
        Table table = retrievalTable(database, tableName);

        buildSelectFields(fields, table);

        if (TableShardingRouter.containsTable(tableName)) {
        	queryResult = ShardsUtils.shardingSelectByIds(asynWriterService, this, ids, tableName, fields);
        	return queryResult;
        }else{
        	QueryCriteria queryCriteria = new QueryCriteria();
        	List<Long> idd = new ArrayList<>();
        	for(Object id:ids){
        		idd.add((Long)id);
        	}
        	Criteria criteria = queryCriteria.createCriteria();
        	criteria.andColumnIn(BaseDTO.ID, idd);
        	table.setQueryCriteria(queryCriteria);
            result = _selectByCriteria(table);
        }

        if (result != null) {
            if (isCacheResult(0, tableName, result.size())) {
                cacheManager.getCache().putObject(buildTableCacheSpace(database, tableName), cacheKey, result);
            }
            return queryResult;
        } else {
            return null;
        }
    }
    
    
    
    @Override
    public Object save(Object obj) {
        return save(new Model(obj));
    }
    
    private Object save(Model model) {
        EventManager.getInstance().sendEvent(new EventContext(Method.SAVE, true, model));
        if (router != null) {
            router.routeToMaster();
        }
        
        Table table = retrievalTableByModel(model);
        String tableName = model.getTableName();
        Object idObj = null;
        if(table.getContent().hasForeignKeys()) {
        	ForeignKey foreignKey = BeanUtil.getBeafForeignKey(table.getContent().getForeignKeys(), model.getContent());
        	while(null != foreignKey) {
        		Model mod = model.newForeignKeyModel(foreignKey);
        		if(mod.hasPrimaryKey()) {
        			idObj = updateByPrimaryKey(mod);
        		}else {
        			idObj = insert(mod);
        		}
        		foreignKey.setId(idObj);
                foreignKey = BeanUtil.getBeafForeignKey(table.getContent().getForeignKeys(), model.getContent());
                if(null == foreignKey) {
                	break;
                }
        	}
        	model.processForeignKey(table.getContent().getForeignKeys());
        	if(model.hasPrimaryKey()) {
    			idObj = updateByPrimaryKey(model);
    		}else {
    			idObj = insert(model);
    		}
        }else {
        	if(model.hasPrimaryKey()) {
    			idObj = updateByPrimaryKey(model);
    		}else {
    			idObj = insert(model);
    		}
        }

        if (isClearTableCache(tableName)) {
            String cacheKey = model.getTableName();
            if (StringUtils.isNotBlank(model.getDatabase())) {
                cacheKey = CACHE_KEY_PREFIX + model.getDatabase() + "#" + cacheKey;
            } else {
                cacheKey = CACHE_KEY_PREFIX + cacheKey;
            }
            asynClear(cacheKey + CACHE_KEY_SELECT_BY_CRITERIA);
            asynClear(cacheKey + CACHE_KEY_COUNT_BY_CRITERIA);
            cacheKey += CACHE_KEY_SELECT_BY_PRIMARY_KEY + model.getSinglePrimaryKey();
            asynClear(cacheKey);
        }

        EventManager.getInstance().sendEvent(new EventContext(Method.INSERT, false, model));
        return idObj;
    }

    
	//-------------------------------------------------------------------
	// 业务核心方法实现-insert
	//-------------------------------------------------------------------
    
    @Override
    public Object insert(Object obj) {
        return insert(new Model(obj));
    }

    @Override
    public Object insert(String table, Map<String, Object> obj) {
        Model model = new Model(table);
        model.addContent(obj);
        return insert(model);
    }

    @Override
    public Object insert(String database, String table, Map<String, Object> obj) {
        Model model = new Model(database, table);
        model.addContent(obj);
        return insert(model);
    }

    private Object insert(Model model) {
        EventManager.getInstance().sendEvent(new EventContext(Method.INSERT, true, model));
        if (router != null) {
            router.routeToMaster();
        }

        if (TableShardingRouter.containsTable(model.getTableName())) {
            long id = 0L;
            if (model.getContent()!= null && model.getContent().size() > 0
                    &&  model.getContent().get(BaseDTO.ID) != null){
                id =  Long.valueOf(model.getContent().get(BaseDTO.ID).toString());
            }
            if (id == 0) {
                 TableShardingRouter.generateShardingId(model);
                 id = Long.valueOf( model.getSinglePrimaryKey().toString());
            }
            model.getContent().put(BaseDTO.ID, id);

            String shardingTable = TableShardingRouter.getShardingTableById(model.getTableName(), id);
            if (StringUtils.isNotEmpty(shardingTable)) {
                model.setTableName(shardingTable);
            }
        }

        Table table = retrievalTableByModel(model);
        String tableName = model.getTableName();
        Object idObj = null;
    	if (model != null && model.getContent() != null && model.getContent().size() > 0) {
            table.setParams(model.getContent());
            Long result = _insert(table);
            if (result > 0) {
                idObj = table.getParams().get(BaseDTO.ID);
                if (null == idObj) {
                    idObj = result;
                }
            }
        } else {
            LOG.error(Messages.getString("RuntimeError.8", "model.params"));
            throw new RuntimeException(Messages.getString("RuntimeError.8", "model.params"));
        }

        if (isClearTableCache(tableName)) {
            String cacheKey = model.getTableName();
            if (StringUtils.isNotBlank(model.getDatabase())) {
                cacheKey = CACHE_KEY_PREFIX + model.getDatabase() + "#" + cacheKey;
            } else {
                cacheKey = CACHE_KEY_PREFIX + cacheKey;
            }
            asynClear(cacheKey + CACHE_KEY_SELECT_BY_CRITERIA);
            asynClear(cacheKey + CACHE_KEY_COUNT_BY_CRITERIA);
            cacheKey += CACHE_KEY_SELECT_BY_PRIMARY_KEY + model.getSinglePrimaryKey();
            asynClear(cacheKey);
        }
        //所引表更新
        if (TableShardingRouter.containsTable(model.getTableName())) {
        	Set<String> fields = TableShardingRouter.getIndexTableField(model.getTableName());
        	if(null != fields && fields.size() > 0){
        		ShardsUtils.shardingIndexTableInsert(asynWriterService, this, table.getParams(), table.getTableName());
        	}
        }

        EventManager.getInstance().sendEvent(new EventContext(Method.INSERT, false, model));
        return idObj;
    }
    
    @Override
    public int insertList(String tableName, List<Map<String, Object>> objs) {
    	int result = 0;
    	
    	Model model = new Model(tableName);
    	Table table = retrievalTableByModel(model);
    	
    	if(objs != null && objs.size() > 0){
            if (router != null) {
                router.routeToMaster();
            }
    	}
    	
    	if (TableShardingRouter.containsTable(model.getTableName())) {
            result = ShardsUtils.shardingBatchInsert(asynWriterService, this, objs, model.getTableName());
		}else{
            if (model != null && objs != null && objs.size() > 0) {
            	List<Map<String, Object>> list = JsonUtils.objListToMapList(objs);
                table.setBatchs(list);
            } else {
                LOG.error(Messages.getString("RuntimeError.8", "model.params"));
                throw new RuntimeException(Messages.getString("RuntimeError.8", "model.params"));
            }
            result = _insertList(table);
		}
        
        if (isClearTableCache(tableName)) {
            String cacheKey = model.getTableName();
            if (StringUtils.isNotBlank(model.getDatabase())) {
                cacheKey = CACHE_KEY_PREFIX + model.getDatabase() + "#" + cacheKey;
            } else {
                cacheKey = CACHE_KEY_PREFIX + cacheKey;
            }
            asynClear(cacheKey + CACHE_KEY_SELECT_BY_CRITERIA);
            asynClear(cacheKey + CACHE_KEY_COUNT_BY_CRITERIA);
            asynClear(cacheKey);
        }
        
        EventManager.getInstance().sendEvent(new EventContext(Method.INSERT_BATCH, false, model));
        
        return result;
    }
    
    @Override
    public int insertList(List<?> objs) {
    	int result = 0;
    	if(objs != null && objs.size() > 0){
            
            Object obj = objs.get(0);
    		Model model = new Model(obj);
            String tableName = model.getTableName();
            List<Map<String, Object>> list = JsonUtils.objListToMapList(objs);
            
            result = insertList(tableName, list);
    	}
    	return result;
    }

    @Override
    public void asynInsert(Object obj) {
        sqlQueue.offer(new AsynContext(Method.INSERT, obj));
    }

    @Override
    public void asynInsert(String table, Map<String, Object> obj) {
        sqlQueue.offer(new AsynContext(Method.INSERT_TABLE, table, obj));
    }

    @Override
    public void asynInsert(String database, String table, Map<String, Object> obj) {
        sqlQueue.offer(new AsynContext(Method.INSERT_DATABASE_TABLE, database, table, obj));
    }


	//-------------------------------------------------------------------
	// 业务核心方法实现-updateByCriteria
	//-------------------------------------------------------------------

    @Override
    public int updateByCriteria(Object obj, QueryCriteria queryCriteria) {
        return updateByCriteria(new Model(obj), queryCriteria);
    }

    private int updateByCriteria(Model model, QueryCriteria queryCriteria) {
        EventManager.getInstance().sendEvent(new EventContext(Method.UPDATE, true, model, queryCriteria));
        if (queryCriteria != null && queryCriteria.getOredCriteria() != null && queryCriteria.getOredCriteria().size() > 0) {
            if (router != null) {
                router.routeToMaster();
            }

            Table table = retrievalTableByQueryCriteria(queryCriteria);
            String tableName = queryCriteria.getTable();
            if (model != null && model.getContent() != null && model.getContent().size() > 0) {
                table.setParams(model.getContent());
            } else {
                LOG.error(Messages.getString("RuntimeError.8", "model.params"));
                throw new RuntimeException(Messages.getString("RuntimeError.8", "model.params"));
            }

            if (containsTables(tableName) == false) {
                table.getContent().setVersionField(null);
            }
            int result = 0;
            if (TableShardingRouter.containsTable(tableName)) {
                if(queryCriteria.getShardTransaction()){
                    result = ShardsUtils.shardingBatchUpdate(this,model,queryCriteria);
                }else{
                    result = ShardsUtils.shardingBatchUpdate(asynWriterService, this, model, queryCriteria, Method.UPDATE_BY_CRITERIA);
                }
            } else {
                table.setQueryCriteria(queryCriteria);
                result = _updateByCriteria(table);
            }

            if (isClearTableCache(tableName)) {
                String cacheKey = tableName;
                if (StringUtils.isNotBlank(model.getDatabase())) {
                    cacheKey = CACHE_KEY_PREFIX + queryCriteria.getDatabase() + "#" + cacheKey;
                } else {
                    cacheKey = CACHE_KEY_PREFIX + cacheKey;
                }
                asynClear(cacheKey + CACHE_KEY_SELECT_BY_CRITERIA);
                asynClear(cacheKey + CACHE_KEY_COUNT_BY_CRITERIA);
                asynClear(cacheKey + CACHE_KEY_SELECT_BY_PRIMARY_KEY);
            }
            EventManager.getInstance().sendEvent(new EventContext(Method.UPDATE, false, model, queryCriteria));
            return result;
        }
        return 0;
    }

	//-------------------------------------------------------------------
	// 业务核心方法实现-updateByPrimaryKey
	//-------------------------------------------------------------------
    @Override
    public int updateByPrimaryKey(Object obj) {
        return updateByPrimaryKey(new Model(obj));
    }

    @Override
    public int updateByPrimaryKey(String table, Map<String, Object> obj) {
        Model model = new Model(table);
        model.addContent(obj);
        return updateByPrimaryKey(model);
    }

    @Override
    public int updateByPrimaryKey(String database, String table, Map<String, Object> obj) {
        Model model = new Model(database, table);
        model.addContent(obj);
        return updateByPrimaryKey(model);
    }
    
    @Override
    public void asynUpdateByPrimaryKey(String table, Map<String, Object> obj) {
        sqlQueue.offer(new AsynContext(Method.UPDATE_BY_PRIMARY_KEY, table, obj));
    }

    private int updateByPrimaryKey(Model model) {
        EventManager.getInstance().sendEvent(new EventContext(Method.UPDATE, true, model));
        if (router != null) {
            router.routeToMaster();
        }

        if (TableShardingRouter.containsTable(model.getTableName())) {
            long id = Long.valueOf(model.getSinglePrimaryKey().toString());
            String shardingTable = TableShardingRouter.getShardingTableById(model.getTableName(), id);
            if (StringUtils.isNotEmpty(shardingTable)) {
                model.setTableName(shardingTable);
            }
        }

        Table table = retrievalTableByModel(model);
        String tableName = model.getTableName();
        LinkedHashMap<String, Object> params = new LinkedHashMap<String, Object>();
        LinkedHashMap<String, Object> conditions = new LinkedHashMap<String, Object>();
        if (model != null && model.getContent() != null && model.getContent().size() > 0) {
            List<String> names = table.getPrimaryKey().getFields();
            if (null != model.getSinglePrimaryKey() && names.size() == 1) {
            	Object id = model.getContent().get(names.get(0));
            	if(id == null) {
            		id = model.getSinglePrimaryKey();
            	}
                model.getContent().put(names.get(0), id);
            }
            Iterator<String> iter = model.getContent().keySet().iterator();
            while (iter.hasNext()) {
                String key = iter.next();
                Object value = model.getContent().get(key);
                if (null != value) {
                    Column column = table.getField(key);
                    if (column != null) {
                        JavaType javaType = JavaTypeResolver.calculateJavaType(table.getField(key).getJdbcType());
                        if (table.getPrimaryKey().getFields().contains(key)) {
                            conditions.put(key, JavaTypeConversion.convert(javaType, value));
                        } else {
                            params.put(key, JavaTypeConversion.convert(javaType, value));
                        }
                    }
                }
            }
            if (containsTables(model.getTableName()) == false) {
                table.getContent().setVersionField(null);
            }
            if (table.hasVersion()) {
                Object value = model.getVersion();
                if (null == value) {
                    throw new DalSqlException("Version is request.");
                }
                conditions.put(version, value);
                model.getContent().remove(Model.VERSION);
            }
            table.setParams(params);
            table.setConditions(conditions);
        } else {
            LOG.error(Messages.getString("RuntimeError.8", "model.params"));
            throw new RuntimeException(Messages.getString("RuntimeError.8", "model.params"));
        }
        
        int result = 0;
        boolean transaction = false;
        /*if(ThreadLocalCacheManager.isTransaction()){
            if (table.hasVersion()) {
                model.getContent().put(version, table.getConditions().get(version));
            }
        	transaction = ThreadLocalCacheManager.putUpdate(model);
            ThreadLocalCacheManager.setBaseDAL(this);
        }*/
        //ThreadLocalCacheManager.putUpdate(model);
        if(!transaction){
        	result = _updateByPrimaryKey(table);
        }

        if (isClearTableCache(tableName)) {
            String cacheKey = model.getTableName();
            if (StringUtils.isNotBlank(model.getDatabase())) {
                cacheKey = CACHE_KEY_PREFIX + model.getDatabase() + "#" + cacheKey;
            } else {
                cacheKey = CACHE_KEY_PREFIX + cacheKey;
            }
            asynClear(cacheKey + CACHE_KEY_SELECT_BY_CRITERIA);
            asynClear(cacheKey + CACHE_KEY_COUNT_BY_CRITERIA);
            cacheKey += CACHE_KEY_SELECT_BY_PRIMARY_KEY + model.getSinglePrimaryKey();
            asynClear(cacheKey);
        }
        
        if (TableShardingRouter.containsTable(model.getTableName())) {
        	Set<String> fields = TableShardingRouter.getIndexTableField(model.getTableName());
        	if(null != fields && fields.size() > 0){
        		Map<String, Object> record = ShardsUtils.buildIndexTableRecord(model.getContent(), model.getTableName());
        		asynUpdateByPrimaryKey(model.getTableName()+"index", record);
        	}
        }

        EventManager.getInstance().sendEvent(new EventContext(Method.UPDATE, false, model));
        return result;
    }


	//-------------------------------------------------------------------
	// 业务核心方法实现-deleteByPrimaryKey
	//-------------------------------------------------------------------
    @Override
    public int deleteByPrimaryKey(Class<?> clazz, Object id) {
        Model model = new Model(clazz);
        model.setSinglePrimaryKey(id);
        return deleteByPrimaryKey(model);
    }

    @Override
    public int deleteByPrimaryKey(String table, Object id) {
        Model model = new Model(table);
        model.setSinglePrimaryKey(id);
        return deleteByPrimaryKey(model);
    }

    @Override
    public int deleteByPrimaryKey(String database, String table, Object id) {
        Model model = new Model(database, table);
        model.setSinglePrimaryKey(id);
        return deleteByPrimaryKey(model);
    }

    @Override
    public int deleteByPrimaryKey(Object obj) {
        return deleteByPrimaryKey(new Model(obj));
    }

    @Override
    public int deleteByPrimaryKey(String table, Map<String, Object> obj) {
        Model model = new Model(table);
        model.addContent(obj);
        return deleteByPrimaryKey(model);
    }

    private int deleteByPrimaryKey(Model model) {
        EventManager.getInstance().sendEvent(new EventContext(Method.DELETE, true, model));
        if (router != null) {
            router.routeToMaster();
        }

        if (TableShardingRouter.containsTable(model.getTableName())) {
            long id = Long.valueOf(model.getSinglePrimaryKey().toString());
            String shardingTable = TableShardingRouter.getShardingTableById(model.getTableName(), id);
            if (StringUtils.isNotEmpty(shardingTable)) {
                model.setTableName(shardingTable);
            }
        }

        Table table = retrievalTableByModel(model);
        String tableName = model.getTableName();
        int result = 0;
        int count = 0;
        if(table.getContent().hasForeignKeys()) {
        	ForeignKey foreignKey = BeanUtil.getBeafForeignKey(table.getContent().getForeignKeys(), model.getContent());
        	while(null != foreignKey && count < 20) {
        		Model mod = model.newForeignKeyModel(foreignKey);
        		result = commonDeleteByPrimaryKey(mod);
        		foreignKey.setId(result);
                foreignKey = BeanUtil.getBeafForeignKey(table.getContent().getForeignKeys(), model.getContent());
                if(null == foreignKey) {
                	break;
                }
                count++;
        	}
        	result = commonDeleteByPrimaryKey(model);
        }else {
        	result = commonDeleteByPrimaryKey(model);
        }
        if (isClearTableCache(tableName)) {
            String cacheKey = model.getTableName();
            if (StringUtils.isNotBlank(model.getDatabase())) {
                cacheKey = CACHE_KEY_PREFIX + model.getDatabase() + "#" + cacheKey;
            } else {
                cacheKey = CACHE_KEY_PREFIX + cacheKey;
            }
            asynClear(cacheKey + CACHE_KEY_SELECT_BY_CRITERIA);
            asynClear(cacheKey + CACHE_KEY_COUNT_BY_CRITERIA);
            cacheKey += CACHE_KEY_SELECT_BY_PRIMARY_KEY + model.getSinglePrimaryKey();
            asynClear(cacheKey);
            //ThreadLocalCacheManager.delect(cacheKey);
        }

        EventManager.getInstance().sendEvent(new EventContext(Method.DELETE, false, model));

        return result;
    }

	private int commonDeleteByPrimaryKey(Model model) {
		int result = 0;
		if(model.hasPrimaryKey()) {
			Table table = retrievalTableByModel(model);
			List<String> names = table.getPrimaryKey().getFields();
	        if (null != model.getSinglePrimaryKey() && names.size() == 1) {
	            LinkedHashMap<String, Object> condistions = new LinkedHashMap<String, Object>();
	            condistions.put(names.get(0), model.getSinglePrimaryKey());
	            table.setConditions(condistions);
	        } else {
	            table.setConditions(model.getContent());
	        }
	        result = _deleteByPrimaryKey(table);
		}
		return result;
	}


	//-------------------------------------------------------------------
	// 业务核心方法实现-deleteByCriteria
	//-------------------------------------------------------------------

    @Override
    public int deleteByCriteria(QueryCriteria queryCriteria) {
        EventManager.getInstance().sendEvent(new EventContext(Method.DELETE, true, queryCriteria));
        if (router != null) {
            router.routeToMaster();
        }
        Table table = retrievalTableByQueryCriteria(queryCriteria);
        String tableName = queryCriteria.getTable();

        int result = 0;
        if (TableShardingRouter.containsTable(queryCriteria.getTable())) {
        	result = ShardsUtils.shardingBatchUpdate(asynWriterService, this, null, queryCriteria, Method.DELETE_BY_CRITERIA);
        } else {
            table.setQueryCriteria(queryCriteria);
            result = _deleteByCriteria(table);
        }

        if (isClearTableCache(tableName)) {
            String cacheKey = queryCriteria.getTable();
            if (StringUtils.isNotEmpty(queryCriteria.getDatabase())) {
                cacheKey = CACHE_KEY_PREFIX + queryCriteria.getDatabase() + "#" + cacheKey;
            } else {
                cacheKey = CACHE_KEY_PREFIX + cacheKey;
            }
            asynClear(cacheKey + CACHE_KEY_SELECT_BY_CRITERIA);
            asynClear(cacheKey + CACHE_KEY_COUNT_BY_CRITERIA);
            asynClear(cacheKey + CACHE_KEY_SELECT_BY_PRIMARY_KEY);
        }
        EventManager.getInstance().sendEvent(new EventContext(Method.DELETE, true, queryCriteria));
        return result;
    }

    @Override
    public void reloadTable(String tableName) {
        reloadTable(null, tableName);
    }

    @Override
    public void clearCache(String tableName) {
        clearCache(null, tableName);
    }

    @Override
    public void reloadTable(String database, String tableName) {
        resolveDatabase.reloadTable(database, tableName);
    }

    @Override
    public void clearCache(String database, String tableName) {
        String cacheKey = tableName;
        if (StringUtils.isNotEmpty(database)) {
            cacheKey = CACHE_KEY_PREFIX + database + "#" + cacheKey;
        } else {
            cacheKey = CACHE_KEY_PREFIX + cacheKey;
        }
        cacheManager.getCache().clear(buildTableCacheSpace(database, tableName), cacheKey + CACHE_KEY_SELECT_BY_CRITERIA);
        cacheManager.getCache().clear(buildTableCacheSpace(database, tableName), cacheKey + CACHE_KEY_COUNT_BY_CRITERIA);
        cacheManager.getCache().clear(buildTableCacheSpace(database, tableName), cacheKey + CACHE_KEY_SELECT_BY_PRIMARY_KEY);
    }

    @Override
    public void clear(String partten) {
        cacheManager.getCache().clear(partten, partten);
    }

    @Override
    public void asynClear(String partten) {
        sqlQueue.offer(new AsynContext(Method.CLEAR, partten, null, null));
    }


    
	//-------------------------------------------------------------------
	// GET/SET方法
	//-------------------------------------------------------------------
    public void setCacheManager(CacheManager cacheManager) {
        this.cacheManager = cacheManager;
    }

    public void setResolveDatabase(ResolveDataBase resolveDatabase) {
        this.resolveDatabase = resolveDatabase;
    }

    public void setRouter(MasterSlaveRouter router) {
        this.router = router;
    }
    
    public void setUseCache(boolean useCache) {
        this.useCache = useCache;
    }

    public void setUseCacheFilter(boolean useCacheFilter) {
        this.useCacheFilter = useCacheFilter;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public void setVersionTables(List<String> versionTables) {
        this.versionTables = versionTables;
    }
    
    public void setUseSQLMerge(boolean flag) {
    	ThreadLocalCacheManager.USE_TRANSACTION = flag;
    }

    public boolean containsTables(String table) {
        if (versionTables == null) {
            return false;
        }
        return versionTables.contains(table);
    }

    public void setListenerNames(List<String> listenerNames) {
        this.listenerNames = listenerNames;
    }

    public void setNoCacheTables(List<String> noCacheTables) {
        this.noCacheTables = noCacheTables;
    }

	public void setCacheTables(List<String> cacheTables) {
        this.cacheTables = cacheTables;
    }
	
	
	//-------------------------------------------------------------------
	// 私有方法
	//-------------------------------------------------------------------
	
	/**
	 * 根据操作对象查询表结构
     * @param database 插入、删除或修改的对象实例
     * @param tableName
     * @return table
     */
    private Table retrievalTable(String database, String tableName) {
        if (database == null || tableName == null ) {
            LOG.error(Messages.getString("RuntimeError.8", "model"));
            throw new RuntimeException(Messages.getString("RuntimeError.8", "model"));
        }
        Table table = resolveDatabase.loadTable(database, tableName, version);
        if (table == null) {
            LOG.error(Messages.getString("RuntimeError.9", tableName));
            throw new RuntimeException(Messages.getString("RuntimeError.9", tableName));
        }
        return table;
    }
	
	/**
	 * 根据操作对象查询表结构
     * @param model 插入、删除或修改的对象实例 
     * @return table
     */
    private Table retrievalTableByModel(Model model) {
        if (model == null || StringUtils.isEmpty(model.getTableName())) {
            LOG.error(Messages.getString("RuntimeError.8", "model"));
            throw new RuntimeException(Messages.getString("RuntimeError.8", "model"));
        }
        Table table = resolveDatabase.loadTable(model.getDatabase(), model.getTableName(), version);
        if (table == null) {
            LOG.error(Messages.getString("RuntimeError.9", model.getTableName()));
            throw new RuntimeException(Messages.getString("RuntimeError.9", model.getTableName()));
        }
        if(null != model.getClazz()) {
        	Set<ForeignKey> fkeys = BeanUtil.loadForeignKeyFromClass(model.getClazz());
            table.getContent().setForeignKeys(fkeys);
        }
        return table;
    }
	
	/**
	 * 根据查询条件查找表结构
     * @param queryCriteria query criteria
     * @return table
     */
    private Table retrievalTableByQueryCriteria(QueryCriteria queryCriteria) {
        if (queryCriteria == null || StringUtils.isEmpty(queryCriteria.getTable())) {
            LOG.error(Messages.getString("RuntimeError.8", "queryCriteria"));
            throw new RuntimeException(Messages.getString("RuntimeError.8", "queryCriteria"));
        }
        Table table = resolveDatabase.loadTable(queryCriteria.getDatabase(), queryCriteria.getTable(), version);
        if (table == null) {
            LOG.error(Messages.getString("RuntimeError.9", queryCriteria.getTable()));
            throw new RuntimeException(Messages.getString("RuntimeError.9", queryCriteria.getTable()));
        }
        return table;
    }
    
    /**
	 * 生成主键查询的缓存key
	 * @param dataBase
     * @param tableName
     * @param ids
	 * @return key
	 */
	private String generateIdsCacheKey(String dataBase, String tableName, Object... ids) {
		String cacheKey = tableName + CACHE_KEY_SELECT_BY_IDS;
		if(null != ids){
			for(Object id:ids){
				cacheKey += (Long)id;
			}
		}
        if (StringUtils.isNotBlank(dataBase)) {
            cacheKey = CACHE_KEY_PREFIX + dataBase + "#" + cacheKey;
        } else {
            cacheKey = CACHE_KEY_PREFIX + cacheKey;
        }
		return cacheKey;
	}
    
    /**
	 * 生成主键查询的缓存key
	 * @param model
	 * @return key
	 */
	private String generateSingleCacheKey(Model model) {
		String cacheKey = model.getTableName() + CACHE_KEY_SELECT_BY_PRIMARY_KEY + model.getSinglePrimaryKey();
        if (StringUtils.isNotBlank(model.getDatabase())) {
            cacheKey = CACHE_KEY_PREFIX + model.getDatabase() + "#" + cacheKey;
        } else {
            cacheKey = CACHE_KEY_PREFIX + cacheKey;
        }
		return cacheKey;
	}
	
	/**
	 * 生成多个查询的缓存key
	 * @param fields 显示字段
	 * @param queryCriteria 查询条件
	 * @param methodType 方法类型
	 * @return key
	 */
	private String generateMultiSelectCacheKey(List<String> fields, QueryCriteria queryCriteria, String methodType) {
		int hashcode = 0;
        if (fields != null) {
            for (String str : fields) {
                hashcode += str.hashCode();
            }
        }
        hashcode += queryCriteria.hashCode();
        String cacheKey = queryCriteria.getTable() + methodType + hashcode;
        if (StringUtils.isNotBlank(queryCriteria.getDatabase())) {
            cacheKey = CACHE_KEY_PREFIX + queryCriteria.getDatabase() + "#" + cacheKey;
        } else {
            cacheKey = CACHE_KEY_PREFIX + cacheKey;
        }
		return cacheKey;
	}
	
	/**
	 * 构建查询字段
	 * <pre>
	 * 对用户传入的查询字段进行过滤，根实际表字段对比，最终以表字段为主
	 * </pre>
	 * @param fields 用户传入的查询字段
	 * @param table 表结构
	 */
	private void buildSelectFields(List<String> fields, Table table) {
		String[] fds = null;
		if(fields != null){
			fds = fields.toArray(new String[fields.size()]);
		}
        buildSelectFields(fds, table);
    }

	/**
	 * 构建查询字段
	 * <pre>
	 * 对用户传入的查询字段进行过滤，根实际表字段对比，最终以表字段为主
	 * </pre>
	 * @param fields 用户传入的查询字段
	 * @param table 表结构
	 */
    private void buildSelectFields(String[] fields, Table table) {
        if (fields != null && fields.length > 0) {
            LinkedHashMap<String, Object> fieldMap = new LinkedHashMap<String, Object>();
            for (String field : fields) {
                if (table.getFields().containsKey(field)) {
                    fieldMap.put(field, true);
                } else {
                    String findfd = "";
                    for (String fd : table.getFields().keySet()) {
                        if (field.indexOf(fd) != -1) {
                            if (fd.length() > findfd.length()) {
                                findfd = fd;
                            }
                        } else if (field.indexOf("count") != -1) {
                            findfd = "_count";
                        }
                    }
                    fieldMap.put(findfd, field);
                }
            }
            table.setParams(fieldMap);
        }
    }
    
    /**
     * 创建二级缓存的空间名称
     */
    private String buildTableCacheSpace(QueryCriteria queryCriteria){
    	 return buildTableCacheSpace(queryCriteria.getDatabase(), queryCriteria.getTable());
    }
    
    /**
     * 创建二级缓存的空间名称
     */
    private String buildTableCacheSpace(Model model){
    	 return buildTableCacheSpace(model.getDatabase(), model.getTableName());
    }
    
    /**
     * 创建二级缓存的空间名称
     * @param database 数据库
     * @param table 表名
     * @return 空间名称
     */
    private String buildTableCacheSpace(String database, String table){
    	 if (StringUtils.isNotBlank(database)) {
    		 return CACHE_KEY_PREFIX + database + "_" + table;
         } else {
        	 return CACHE_KEY_PREFIX + table;
         }
    }


    /**
     * 是否需要缓存这个表
     * @param seconds
     * @param tableName
     * @return
     */
    private boolean isCacheTable(int seconds, String tableName) {
        if (cacheManager != null && seconds != NO_CACHE && useCache && (!useCacheFilter && noCacheTables != null && !noCacheTables.contains(tableName) || useCacheFilter && cacheTables != null && cacheTables.contains(tableName))) {
            return true;
        }
        return false;
    }

    /**
     * 是否需要缓存查询结果
     * @param seconds
     * @param tableName
     * @param result
     * @return
     */
    private boolean isCacheResult(int seconds, String tableName, int result) {
        if (isCacheTable(seconds, tableName) && result > 0) {
            return true;
        }
        return false;
    }

    /**
     * 是否需要缓存查询结果
     * @param isCacheTable
     * @param resultSize
     * @return
     */
    private boolean isCacheResult(boolean isCacheTable, int resultSize) {
        if (isCacheTable && resultSize > 0) {
            return true;
        }
        return false;
    }

    /**
     * 是否需要清除缓存
     * @param tableName
     * @return
     */
    private boolean isClearTableCache(String tableName){
        if(cacheManager != null && useCache && (!useCacheFilter && noCacheTables != null && !noCacheTables.contains(tableName) || useCacheFilter && cacheTables != null && cacheTables.contains(tableName))){
            return true;
        }
        return false;
    }

    public void init() {
    }

}
